package order

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/common/jwt"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/subscribex"
	"mall/common/uniqueidx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/order/rpc/orderclient"
	"mall/service/user/rpc/userclient"

	// "github.com/sirupsen/logrus"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"github.com/zeromicro/go-zero/core/logx"
)

type PayOrderLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewPayOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PayOrderLogic {
	return &PayOrderLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *PayOrderLogic) PayOrder(
	req *types.PayOrderReq,
	headers *[]*types.Header,
) (resp *types.PayOrderResp, err error) {
	if req.OrderId == nil || *req.OrderId == 0 {
		return nil, resultx.NewErrCode(resultx.ORDER_ID_REQUIRED)
	}

	var paymentId uint32
	if req.PaymentId != nil && *req.PaymentId > 0 {
		switch *req.PaymentId {
		case globalkey.PaymentTypeWeChatPay, globalkey.PaymentTypeGems:
			paymentId = *req.PaymentId
		}
	} else {
		paymentId = globalkey.PaymentTypeWeChatPay
	}

	if paymentId == 0 {
		return nil, resultx.NewErrCode(resultx.ORDER_PAYMENT_ID_INVALID)
	}

	// 1.  get user openId
	userId := jwt.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	userAuthInfo, err := l.fetchUserAuth(userId)
	if err != nil {
		return nil, err
	}

	var openId string
	if userAuthInfo.Openid != nil {
		openId = *userAuthInfo.Openid
	}

	// 2. 查找订单
	preload := "order:Id,Sn,ActualAmount,RemainingAmount"
	preload += ";orderItem:Id,ProductId,SkuId,Quantity,Title"
	eagerLoad := "order:orderItem"
	var filters []string
	filters = append(filters, "Id,equalTo:"+strconv.FormatInt(int64(*req.OrderId), 10))
	filters = append(filters, "UserId,equalTo:"+strconv.FormatInt(int64(userId), 10))
	filters = append(filters, "TradeState,equalTo:"+strconv.FormatInt(int64(globalkey.OrderTradeStateNotPay), 10))
	var platformId uint32 = globalkey.ThirdPartyPlatformWechatMini

	order, err := l.svcCtx.OrderRpc.GetOrderById(l.ctx, &orderclient.BaseIdReq{
		Preload:    pointy.GetPointer(preload),
		EagerLoad:  pointy.GetPointer(eagerLoad),
		Filter:     pointy.GetPointer(strings.Join(filters, ";")),
		VisitorId:  pointy.GetPointer(userId),
		PlatformId: pointy.GetPointer(platformId),
	})
	if err != nil {
		logx.Errorw("PayOrder GetOrderById err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.ORDER_PARAM_INVALID)
	}

	if order == nil || order.Id == nil || order.ActualAmount == nil || *order.Id == 0 || len(*order.ActualAmount) == 0 || len(order.Skus) == 0 {
		return nil, resultx.NewErrCode(resultx.ORDER_PARAM_INVALID)
	}

	var description string
	var count int
	var skus []*types.OrderSkuInfo
	if *order.OrderType != globalkey.OrderTypeVirtual {
		for _, v := range order.Skus {
			if v.SkuId != nil && v.Quantity != nil && *v.SkuId > 0 && *v.Quantity > 0 {
				count += int(*v.Quantity)
				if len(description) == 0 && v.Title != nil {
					titleRune := []rune(strings.TrimSpace(*v.Title))
					if len(titleRune) > 38 {
						description = string(titleRune[0:38])
					} else {
						description = strings.TrimSpace(*v.Title)
					}
				}

				// skus = append(skus, &types.OrderSkuInfo{
				// 	Id:        v.Id,
				// 	ProductId: v.ProductId,
				// 	SkuId:     v.SkuId,
				// 	Quantity:  v.Quantity,
				// 	Title:     v.Title,
				// })
			}
		}
	}
	if len(order.Skus) > 1 && len(description) > 0 {
		description += fmt.Sprintf(" 等%d件商品", count)
	}

	var finalAmountFen int64
	var finalAmount *string
	if fa, err := moneyx.ParseDecimal(*order.ActualAmount); err != nil {
		return nil, resultx.NewErrCode(resultx.ORDER_PARAM_INVALID)
	} else {
		finalAmountFen = moneyx.FormatDecimalToFen(fa)
		finalAmount = order.ActualAmount

		// logrus.Info(fmt.Sprintf("PayOrder ActualAmount: %+v", *order.ActualAmount))
		// logrus.Info(fmt.Sprintf("PayOrder finalAmount: %+v", fa))
		// logrus.Info(fmt.Sprintf("PayOrder finalAmountFen: %+v", finalAmountFen))

		if userId == 10021 || userId == 10035 { // 测试账号
			finalAmountFen = 2
			finalAmount = pointy.GetPointer("0.02")
		}
	}

	var sn string
	var payment *orderclient.OrderPaymentInfo
	if paymentId == globalkey.PaymentTypeWeChatPay {
		// 微信支付
		sn = uniqueidx.GenSn(uniqueidx.SN_PREFIX_THIRD_PART_PAYMENT)
		payment = &orderclient.OrderPaymentInfo{
			No:     pointy.GetPointer(sn),
			Amount: finalAmount,
			Type:   pointy.GetPointer(globalkey.PaymentTypeWeChatPay),
			Name:   pointy.GetPointer("微信支付"),
			Code:   pointy.GetPointer("WeChatPay"),
			Status: pointy.GetPointer(globalkey.PaymentStatusNotPay),
		}
	} else if paymentId == globalkey.PaymentTypeGems {
		// 宝石支付
		sn = uniqueidx.GenSn(uniqueidx.SN_PREFIX_GEM)
		payment = &orderclient.OrderPaymentInfo{
			No:     pointy.GetPointer(sn),
			Amount: finalAmount,
			Type:   pointy.GetPointer(globalkey.PaymentTypeGems),
			Name:   pointy.GetPointer("宝石"),
			Code:   pointy.GetPointer("Gems"),
			Status: pointy.GetPointer(globalkey.PaymentStatusNotPay),
		}
	} else {
		return nil, resultx.NewErrCode(resultx.ORDER_PARAM_INVALID)
	}

	// 3. 修改订单
	_, err = l.svcCtx.OrderRpc.UpdateOrder(l.ctx, &orderclient.OrderInfo{
		Id:         order.Id,
		UserId:     pointy.GetPointer(userId),
		NewPayment: payment,
	})
	if err != nil {
		return nil, err
	}

	tradeState := pointy.GetPointer(globalkey.OrderTradeStatePaid)
	option := util.GetOptionByStatus(tradeState)

	// 4. 获取支付参数
	return &types.PayOrderResp{
		CreateOrderResp: types.CreateOrderResp{
			OrderId:   order.Id,
			OrderSn:   order.Sn,
			PayParams: l.getPayParams(paymentId, sn, finalAmountFen, openId, description, skus),
			STID:      pointy.GetPointer(subscribex.ForumOrderShipmentTemplateID),
		},
		Option: &types.TradeInfoFragment{
			TradeState:     tradeState,
			TradeStateText: util.GetTradeStateText(tradeState),
			CanCancel:      option.CanCancel,
			CanPay:         option.CanPay,
			CanRefund:      option.CanRefund,
			CanRemind:      option.CanRemind,
			CanTrack:       option.CanTrack,
			CanConfirm:     option.CanConfirm,
			CanInvoice:     option.CanInvoice,
			CanRemove:      option.CanRemove,
		},
	}, nil
}

func (l *PayOrderLogic) fetchUserAuth(userId uint64) (*userclient.TmpUserAuthInfo, error) {
	res, err := l.svcCtx.UserRpc.GetUserAuthByUserId(l.ctx, &userclient.GetUserAuthByUserIdReq{
		PlatformId: pointy.GetPointer(globalkey.ThirdPartyPlatformWechatMini),
		UserId:     pointy.GetPointer(userId),
	})
	if err != nil {
		logx.Infow("PayOrder fetchUserAuth err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	if res.Openid == nil {
		return nil, resultx.NewErrCode(resultx.USER_GET_INFO_FAILED)
	}

	return res, nil
}

func (l *PayOrderLogic) getPayParams(
	paymentId uint32,
	sn string,
	amount int64,
	openId string,
	description string,
	skus []*types.OrderSkuInfo,
) (payParams *types.ThirdPartyPaymentInfo) {
	// 4. 获取支付参数
	if paymentId == globalkey.PaymentTypeWeChatPay {
		payParams = &types.ThirdPartyPaymentInfo{}
		if wxPayInfo, err := l.getWxPayParams(sn, amount, openId, description, skus); err == nil {
			payParams.WxPayInfo = wxPayInfo
		}
	}

	return
}

func (l *PayOrderLogic) getWxPayParams(
	sn string,
	amount int64,
	openId string,
	description string,
	skus []*types.OrderSkuInfo,
) (*types.ThirdPartyPaymentWxPayInfo, error) {
	res, err := l.createWxPrePayOrder(sn, amount, openId, description, skus)
	if err != nil {
		return nil, err
	}

	return &types.ThirdPartyPaymentWxPayInfo{
		Appid:     pointy.GetPointer(l.svcCtx.Config.WxMiniConf.AppId),
		NonceStr:  res.NonceStr,
		PaySign:   res.PaySign,
		Package:   res.Package,
		Timestamp: res.TimeStamp,
		SignType:  res.SignType,
	}, nil
}

func (l *PayOrderLogic) createWxPrePayOrder(
	sn string,
	amount int64,
	openId string,
	description string,
	skus []*types.OrderSkuInfo,
) (*jsapi.PrepayWithRequestPaymentResponse, error) {
	wxPayClient, err := svc.NewWxPayClientV3(l.svcCtx.Config)
	if err != nil {
		logx.Errorw("create wechat pay prepayorder, init err", logx.Field("detail", err.Error()))
		return nil, resultx.NewErrCode(resultx.PAYMENT_WECHAT_PAY_INIT_PRE_ORDER_FAIL)
	}

	jsApiSvc := jsapi.JsapiApiService{Client: wxPayClient}

	var goodsDetail *jsapi.Detail
	if len(skus) > 0 {
		var goodsDetails []jsapi.GoodsDetail // 优惠功能-单品列表
		for _, v := range skus {
			if v.Title != nil && len(strings.TrimSpace(*v.Title)) > 0 {
				goodsDetails = append(goodsDetails, jsapi.GoodsDetail{
					GoodsName: core.String(strings.TrimSpace(*v.Title)), //商品名称
					// MerchantGoodsId:  core.String("ABC"), //商户侧商品编码
					// Quantity:         core.Int64(1),
					// UnitPrice:        core.Int64(828800),
					// WechatpayGoodsId: core.String("1001"), //微信支付商品编码,微信支付定义的统一商品编号（没有可不传）
				})
			}
		}
		if len(goodsDetails) > 0 {
			goodsDetail = &jsapi.Detail{
				GoodsDetail: goodsDetails,
			}
		}
	}

	// Get the prepay_id, as well as the parameters and signatures needed to invoke the payment
	//PrepayWithRequestPayment Jsapi支付下单，并返回调起支付的请求参数
	resp, result, err := jsApiSvc.PrepayWithRequestPayment(l.ctx, jsapi.PrepayRequest{
		Appid:       core.String(l.svcCtx.Config.WxMiniConf.AppId),
		Mchid:       core.String(l.svcCtx.Config.WxPayConf.MchId),
		Description: core.String(description),
		OutTradeNo:  core.String(sn),
		// TimeExpire:    core.Time(time.Now()),
		Attach: core.String(description),
		// Attach:          core.String("自定义数据说明"),
		NotifyUrl: core.String(l.svcCtx.Config.WxPayConf.NotifyUrl),
		// GoodsTag:      core.String("WXG"),
		// LimitPay:      []string{"LimitPay_example"},
		// SupportFapiao: core.Bool(false),
		Amount: &jsapi.Amount{
			Currency: core.String("CNY"),
			Total:    core.Int64(amount),
		},
		Payer: &jsapi.Payer{
			Openid: core.String(openId),
		},
		Detail: goodsDetail,
		// Detail: &jsapi.Detail{
		// 	CostPrice: core.Int64(608800),
		// 		GoodsDetail: goodsDetails,
		// 	InvoiceId: core.String("wx123"),
		// },
		// SceneInfo: &jsapi.SceneInfo{
		// 	DeviceId:      core.String("013467007045764"),
		// 	PayerClientIp: core.String("14.23.150.211"),
		// 	StoreInfo: &jsapi.StoreInfo{
		// 		Address:  core.String("广东省深圳市南山区科技中一道10000号"),
		// 		AreaCode: core.String("440305"),
		// 		Id:       core.String("0001"),
		// 		Name:     core.String("腾讯大厦分店"),
		// 	},
		// },
		// SettleInfo: &jsapi.SettleInfo{
		// 	ProfitSharing: core.Bool(false),
		// },
	})
	if err != nil {
		//处理错误
		logx.Errorw("call Prepay err", logx.Field("detail", err.Error()))
		// error http response:[StatusCode: 400 Code: \"PARAM_ERROR\"\nMessage: 输入源“/body/amount/total”映射到数值字段“总金额”规则校验失败>，值低于最小值 1\nDetail:\n{\n  \"location\": \"body\",\n  \"value\": 0\n}\nHeader:\n - Content-Language=[zh-CN]\n - Date=[Mon, 07 Oct 2024 05:17:52 GMT]\n - Connection=[keep-alive]\n - Keep-Alive=[timeout=8]\n - X-Content-Type-Options=[nosniff]\n - Request-Id=[0880DE8DB806103418CECC8C58208D840828EAA505-268435464]\n - Server=[nginx]\n - Content-Type=[application/json; charset=utf-8]\n - Content-Length=[184]\n - Cache-Control=[no-cache, must-revalidate]]
		return nil, resultx.NewErrCode(resultx.PAYMENT_WECHAT_PAY_INIT_PRE_ORDER_FAIL)
	} else {
		//处理返回结果
		logx.Infow(
			"call Prepay",
			logx.Field("status", result.Response.StatusCode),
			logx.Field("resp", resp),
		)
	}

	return resp, nil
}
